﻿using System;
using System.Collections.ObjectModel;

namespace Needle.Utils.Collections
{

    /// <summary></summary>
    public class ObservableRichCollection<T> : RichCollection<T>
    {
        /// <summary></summary>
        public ObservableRichCollection()
        { }

        /// <summary></summary>
        public ObservableRichCollection(IRichList<T> list)
            : base(list)
        { }

        /// <summary>
        /// Raised when the collection is about to change
        /// </summary>
        public event EventHandler<CollectionChangeEventArgs<T>> CollectionChanging;
        /// <summary>
        /// Used to raise the CollectionChanging event.
        /// </summary>
        /// <param name="index">The index that is about to change or -1 in case of a clear.</param>
        /// <param name="change">The change that is about to happen.</param>
        /// <param name="item">The item that is involved in the change, or default(T) in case of a clear.</param>
        protected virtual void OnCollectionChanging(int index, CollectionChange change, T item)
        {
            if (_CanRaiseEvents && CollectionChanging != null)
            {
                CollectionChanging(this, new CollectionChangeEventArgs<T>(index, change, item));
            }
        }
        /// <summary>
        /// Raised when the collection has been changed
        /// </summary>
        public event EventHandler<CollectionChangeEventArgs<T>> CollectionChanged;
        /// <summary>
        /// Used to raise the CollectionChanged event.
        /// </summary>
        /// <param name="index">The index that has changed or -1 in case of a clear.</param>
        /// <param name="change">The change that happened.</param>
        /// <param name="item">The item that was involved in the change, or default(T) in case of a clear.</param>
        protected virtual void OnCollectionChanged(int index, CollectionChange change, T item)
        {
            if (_CanRaiseEvents && CollectionChanged != null)
            {
                CollectionChanged(this, new CollectionChangeEventArgs<T>(index, change, item));
            }
        }
        /// <summary></summary>
        protected override void ClearItems()
        {
            OnCollectionChanging(-1, CollectionChange.Clearing, default(T));
            base.ClearItems();
            OnCollectionChanged(-1, CollectionChange.Cleared, default(T));
        }
        /// <summary></summary>
        protected override void RemoveItem(int index)
        {
            T oldItem = this[index];
            OnCollectionChanging(index, CollectionChange.Removing, oldItem);
            base.RemoveItem(index);
            OnCollectionChanged(index, CollectionChange.Removed, oldItem);
        }
        /// <summary></summary>
        protected override void InsertItem(int index, T item)
        {
            OnCollectionChanging(index, CollectionChange.Inserting, item);
            base.InsertItem(index, item);
            OnCollectionChanged(index, CollectionChange.Inserted, item);
        }
        /// <summary></summary>
        protected override void SetItem(int index, T item)
        {
            T oldItem = this[index];
            OnCollectionChanging(index, CollectionChange.Removing, oldItem);
            OnCollectionChanging(index, CollectionChange.Inserting, item);
            base.SetItem(index, item);
            OnCollectionChanged(index, CollectionChange.Removed, oldItem);
            OnCollectionChanged(index, CollectionChange.Inserted, item);
        }

        private bool _CanRaiseEvents = true;
        /// <summary>
        /// True when events are raised, false when events are not raised.
        /// </summary>
        public bool CanRaiseEvents
        {
            get { return _CanRaiseEvents; }
            set { _CanRaiseEvents = value; }
        }

    }
    /// <summary>
    /// Collection change event argument
    /// </summary>
    /// <typeparam name="T">the type of the item.</typeparam>
    public class CollectionChangeEventArgs<T> : EventArgs
    {
        /// <summary>
        /// Initializes a new instance of the CollectionChangeEventArgs class.
        /// </summary>
        /// <param name="index">The index of the change, or -1 when clearing.</param>
        /// <param name="change">The type of change.</param>
        /// <param name="item">The item or default(T) when clearing.</param>
        public CollectionChangeEventArgs(int index, CollectionChange change, T item)
        {
            _Change = change;
            _Index = index;
            _Item = item;
        }
        private CollectionChange _Change;
        /// <summary>
        /// The type of the change
        /// </summary>
        public CollectionChange Change
        {
            get { return _Change; }
        }

        private int _Index;
        /// <summary>
        /// The index of the change, or -1 when clearing
        /// </summary>
        public int Index
        {
            get { return _Index; }
        }

        private T _Item;
        /// <summary>
        /// The item or default(T) when clearing.
        /// </summary>
        public T Item
        {
            get { return _Item; }
        }
    }

    /// <summary>
    /// The collection change type
    /// </summary>
    public enum CollectionChange
    {
        /// <summary>
        /// The collection is about to be cleared.
        /// </summary>
        Clearing,
        /// <summary>
        /// An item is about to be removed.
        /// </summary>
        Removing,
        /// <summary>
        /// An item is about to be inserted.
        /// </summary>
        Inserting,
        /// <summary>
        /// The collection has been cleared.
        /// </summary>
        Cleared,
        /// <summary>
        /// An item has been removed.
        /// </summary>
        Removed,
        /// <summary>
        /// An item has been inserted.
        /// </summary>
        Inserted,
    }
}
